#include "csr.h"
#include "print.h"
#include "timer.h"
#include "plic.h"
#include "process.h"
#include "mmu.h"

#define S_SOFT_INT      (1)
#define M_SOFT_INT      (3)
#define S_TIMER_INT     (5)
#define M_TIMER_INT     (7)
#define S_EXT_INT       (9)
#define M_EXT_INT       (11)

#define INSTRUCTION_ADDR_MISALIGNED     (0)
#define INSTRUCTION_ACCESS_FAULT        (1)
#define ILLEGAL_INSTRUCTION             (2)
#define BREAK_POINT                     (3)
#define LOAD_ADDR_MISALIGNED            (4)
#define LOAD_ACCESS_FAULT               (5)
#define STORE_ADDR_MISALIGNED           (6)
#define STORE_ACCESS_FAULT              (7)
#define ECALL_FROM_UMODE                (8)
#define ECALL_FROM_SMODE                (9)
#define ECALL_FROM_MMODE                (11)
#define INSTRUCTION_PAGE_FAULT          (12)
#define LOAD_PAGE_FAULT                 (13)
#define STORE_PAGE_FAULT                (15)

static char *interrupt_cause[] = {
    "Reserved",
    "Supervisor software interrupt",
    "Reserved",
    "Machine software interrupt",
    "Reserved",
    "Supervisor timer interrupt",
    "Reserved",
    "Machine timer interrupt",
    "Reserved",
    "Supervisor external interrupt",
    "Reserved",
    "Machine external interrupt",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved"
};

static char *exception_cause[] = {
    "Instruction address misaligned",
    "Instruction access fault",
    "Illegal instruction",
    "Breakpoint",
    "Load address misaligned",
    "Load access fault",
    "Store/AMO address misaligned",
    "Store/AMO access fault",
    "Environment call from U-mode",
    "Environment call from S-mode",
    "Reserved",
    "Environment call from M-mode",
    "Instruction page fault",
    "Load page fault",
    "Reserved",
    "Store/AMO page fault"
};

// interrupts and exceptions from kernel code go here via machine_trap_entry.
void rv_machine_trap(void)
{ 
    unsigned long cause = csr_read(mcause);
    unsigned long mepc  = csr_read(mepc);
    unsigned long tval  = csr_read(mtval);

    int is_int = (cause & (1l << 63l)) ? 1 : 0;
    int mcode = cause & 0xff;

    if (mcode >= 16) {
        pr_err("mtrap %s : %s.\n", is_int ? "Interrupt" : "Exception", "Unknown code");
        return;
    }

    if (is_int) {
        pr_debug("mtrap Interrupt : %s.\n", interrupt_cause[mcode]); 
    } else {
        pr_debug("mtrap Exception : %s.\n", exception_cause[mcode]); 

        switch (mcode) {
        case ILLEGAL_INSTRUCTION:
            pr_debug("mtval = %p\n", tval);
            pr_debug("mepc  = %p\n", mepc);
            break;
        }

        csr_write(mepc, mepc + 4);
    }

    return;
}

void rv_supervisor_trap(void)
{ 
    unsigned long tval  = csr_read(stval);
    unsigned long sepc  = csr_read(sepc);
    unsigned long cause = csr_read(scause);

    int is_int = (cause & (1l << 63l)) ? 1 : 0;
    int scode = cause & 0xff;

    if (scode >= 16) {
        pr_err("strap %s : %s.\n", is_int ? "Interrupt" : "Exception", "Unknown code");
        return;
    }

    if (is_int) {
        unsigned int vector;
        vector = os_vector_get();

        if (vector == 75) {
            // 系统tick处理
            os_timer_int_clear();
#ifdef KERN_TRAP_DEBUG
            pr_debug("tick interrupt.\n");
#endif
            os_proc_tick_handle(); 
        } else {
            pr_debug("strap Interrupt : %s, vector %d.\n", interrupt_cause[scode], vector); 
        }

        os_int_done(vector);

        // 尝试中断中调度
        os_sched_int();
    } else {
        pr_debug("strap Exception : %s.\n", exception_cause[scode]); 
        
        switch (scode) {
        case ILLEGAL_INSTRUCTION:
        case LOAD_ACCESS_FAULT:
        case INSTRUCTION_PAGE_FAULT:
        case LOAD_PAGE_FAULT:
        case STORE_PAGE_FAULT:
            pr_debug("stval = %p\n", tval);
            pr_debug("sepc  = %p\n", sepc);
            panic("fault");
            break;
        }

        csr_write(sepc, sepc + 4);
    }

    return;
}